package com.gitee.hermer.engine.jars.core.app.domain;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.context.ApplicationContext;
import org.springframework.data.annotation.Transient;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.gitee.hermer.boot.jee.commons.collection.CollectionUtils;
import com.gitee.hermer.boot.jee.commons.collection.StringCache;
import com.gitee.hermer.boot.jee.commons.exception.ErrorCode;
import com.gitee.hermer.boot.jee.commons.reflect.ClassUtils;
import com.gitee.hermer.boot.jee.commons.reflect.ReflectUtils;
import com.gitee.hermer.boot.jee.commons.verify.Assert;
import com.gitee.hermer.engine.jars.core.domain.ToStringObject;


/**
 * 模块中实例
 * @author hermer
 *
 */
public class EngineAppContext extends ToStringObject{

	private Map<String, EngineAppMethodBean> jobs = new ConcurrentHashMap<String, EngineAppMethodBean>();
	private Map<String, EngineAppMethodBean> events = new ConcurrentHashMap<String, EngineAppMethodBean>();

	@Transient
	@JsonIgnore
	private transient ApplicationContext context = null;
	@Transient
	@JsonIgnore
	private transient ApplicationContext parentContext = null;

	@Override
	public <T> T getBean(Class<T> c) throws Exception {
		return context.getBean(c);
	}
	
	public <T> T getBean(String beanName) throws Exception{
		return (T) context.getBean(beanName);
	}

	public Set<String> getJobMethods(){
		return jobs.keySet();
	}
	public Collection<EngineAppMethodBean> getEngineBeans(){
		return  jobs.values();
	}

	public EngineAppContext(ApplicationContext context){
		this.context = context;
	}

	public EngineAppContext(ApplicationContext context, ApplicationContext parentContext) {
		this.context = context;
		this.parentContext = parentContext;
	}
	
	public void registerJob(String jobMethod,EngineAppMethodBean appJobMethodBean){
		try{
			String method = generateEngineBeanName(jobMethod,appJobMethodBean.getVersion());
			if(jobs.containsKey(method)){
				debug("[%s]操作方法已经注册... 状态[%s]!",method,"Fail");
			}else{
				jobs.put(method, appJobMethodBean);
				debug("[%s]操作方法注册中... 状态[%s]！",method,"Success");
			}
		}catch (Exception e) {
			error(e.getMessage(),e);
		}
	}
	
	
	public <T> T invokeEngineAppJob(String jobMethod,String version,Object... finArgs) throws Throwable{
		EngineAppMethodBean beanInfo = getEngineJobMethodBean(jobMethod, version);
		Assert.notNullCode(beanInfo, ErrorCode.SYSTEM_ERROR,String.format("没有找到[%s]-[%s]方法！", jobMethod,version));
		return invoke(beanInfo,finArgs);
	}
	private <T> T invoke(EngineAppMethodBean beanInfo,Object... finArgs) throws Throwable {
		Object proxy = ClassUtils.getTargetProxy(context.getBean(beanInfo.getBeanName()));
		return (T) ReflectUtils.invoke(proxy, finArgs, beanInfo.getMethod());
	}

	public EngineAppMethodBean getEngineJobMethodBean(String jobMethod,String version) {
		String method = generateEngineBeanName(jobMethod, version);
		if(jobs.containsKey(method)){
			return jobs.get(method);
		}else{
			Set<String> jobMethods = getJobMethodsStartsWith(jobMethod);
			if(jobMethods.size() == 1){
				String key = jobMethods.iterator().next();
				debug("操作方法[%s]|版本号[%s]|未找到！获取缺省版本方法[%s]...",jobMethod,version,key);
				return jobs.get(key);
			}
			else if(jobMethods.size() > 0){
				error("操作方法[%s]|存在多个版本... 指定|版本号[%s]|未找到！",jobMethod,version);
			}
		}
		return null;
	}

	public Set<String> getJobMethodsStartsWith(String jobMethod){
		Set<String> jobMethods = getJobMethods();
		Set<String> temp = CollectionUtils.newHashSet();
		for (String key : jobMethods) {
			if(key.startsWith(jobMethod)){
				temp.add(key);
			}
		}
		return temp;
	}

	private String generateEngineBeanName(String jobMethod,String version){
		Assert.hasText(version);
		Assert.hasText(jobMethod);
		return new StringCache(jobMethod).append("-").append(version).toString();
	}

	public ApplicationContext getContext() {
		return context;
	}
	public ApplicationContext getParentContext() {
		return parentContext;
	}
	
	public void setContext(ApplicationContext context) {
		this.context = context;
	}
	public void setParentContext(ApplicationContext parentContext) {
		this.parentContext = parentContext;
	}
}
